package com.ms.common.utils.excel;

import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.CellFormat;
import jxl.write.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.view.document.AbstractJExcelView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.*;

public class ExcelView extends AbstractJExcelView {

	/**
	 * 画excel model参数
	 *
	 * @param fileName
	 *            可选，表示导出的文件名，默认为 ‘export’
	 * @param excelData
	 *            可选，表示一个表格，与excelDatas必选其一
	 * @param excelDatas
	 *            可选，表示多个表格，与excelData必选其一
	 */
	@Override
	protected void buildExcelDocument(Map<String, Object> model, WritableWorkbook workbook, HttpServletRequest request,
									  HttpServletResponse response) throws Exception {
		String fileName = (String) model.get("fileName");
		if (StringUtils.isEmpty(fileName)) {
			fileName = "export";
		}
		response.setHeader("Content-Disposition", "attachment;filename="
				+ new String(fileName.getBytes(), "iso-8859-1") + ".xls");

		// 工作簿格式
		WritableCellFormat titleCellFormat = new WritableCellFormat();
		titleCellFormat.setAlignment(Alignment.LEFT);
		titleCellFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

		WritableCellFormat stringCellFormat = new WritableCellFormat();
		stringCellFormat.setAlignment(Alignment.CENTRE);
		stringCellFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

		WritableCellFormat digitCellFormat = new WritableCellFormat();
		digitCellFormat.setAlignment(Alignment.RIGHT);
		digitCellFormat.setBorder(Border.ALL, BorderLineStyle.THIN);

		// 画excel
		WritableSheet ws = workbook.createSheet("Sheet1", 0);
		Map<Integer, CellFormat> cellFormatMap = new HashMap<Integer, CellFormat>();
		cellFormatMap.put(ExcelExportConst.DATA_TYPE_STR, stringCellFormat);
		cellFormatMap.put(ExcelExportConst.DATA_TYPE_DIGIT, digitCellFormat);
		Label label;
		ExcelData[] excelDatas = null;
		ExcelData singleExcelData = (ExcelData) model.get("excelData");
		if (singleExcelData != null) {
			excelDatas = new ExcelData[] { singleExcelData };
		} else {
			excelDatas = (ExcelData[]) model.get("excelDatas");
		}

		int rowIndex = 0;
		for (int i = 0; i < excelDatas.length; i++) {
			// 非第一张表，空一行
			if (i != 0) {
				rowIndex++;
			}
			ExcelData excelData = excelDatas[i];

			// 标题
			if(StringUtils.isNotBlank(excelData.getTitle())) {
				label = new Label(0, rowIndex, excelData.getTitle(), titleCellFormat);
				ws.addCell(label);
				rowIndex++;
			}

			// 表格头
			label = new Label(0, rowIndex, "序号", stringCellFormat);
			ws.addCell(label);
			String[] heads = excelData.getHeads();
			for (int j = 0; j < heads.length; j++) {
				label = new Label(j + 1, rowIndex, heads[j], stringCellFormat);
				ws.addCell(label);
			}
			rowIndex++;

			rowIndex += setData(excelData, heads.length, stringCellFormat, cellFormatMap, ws, rowIndex);

		}

	}

	// 设置表格数据
	private int setData(ExcelData excelData, int columnNum, CellFormat stringCellFormat,
						Map<Integer, CellFormat> cellFormatMap, WritableSheet ws, int currentRowIndex) throws WriteException {
		int rowIndex = 0;
		List<?> dataList = excelData.getDataList();
		Integer[] dataTypes = excelData.getDataTypes();
		if (dataTypes == null) {
			dataTypes = new Integer[columnNum];
			Arrays.fill(dataTypes, ExcelExportConst.DATA_TYPE_STR);
		}
		CellFormat[] cellFormats = new CellFormat[columnNum];
		for (int j = 0; j < cellFormats.length; j++) {
			cellFormats[j] = cellFormatMap.get(dataTypes[j]);
		}

		if (dataList.size() > 0) {
			String[] fieldNames = excelData.getFieldNames();

			if (fieldNames != null) {
				rowIndex = setDataByFieldName(excelData, dataList, stringCellFormat, cellFormats, ws, currentRowIndex);
			} else {
				rowIndex = setDataByFieldIndex(excelData, dataList, stringCellFormat, cellFormats, ws, currentRowIndex);
			}

		}
		return rowIndex;
	}

	@SuppressWarnings("unchecked")
	private int setDataByFieldName(ExcelData excelData, List<?> dataList, CellFormat stringCellFormat,
								   CellFormat[] cellFormats, WritableSheet ws, int currentRowIndex) throws WriteException {
		int rowIndex = 0;
		Class<?> modelClass = excelData.getModelClass();
		if (modelClass == null) {
			modelClass = dataList.get(0).getClass();
		}
		if (modelClass == Map.class) {
			Label label;
			for (int j = 0; j < dataList.size(); j++) {
				Map<String, Object> data = (Map<String, Object>) dataList.get(j);
				label = new Label(0, rowIndex + currentRowIndex, (j + 1) + "", stringCellFormat);
				ws.addCell(label);
				String[] fieldNames = excelData.getFieldNames();
				for (int k = 0; k < fieldNames.length; k++) {
					Object fieldValue = data.get(fieldNames[k]);
					String fieldValueStr = "";
					if (fieldValue != null) {
						fieldValueStr = fieldValue.toString();
					}
					label = new Label(k + 1, rowIndex + currentRowIndex, fieldValueStr, cellFormats[k]);
					ws.addCell(label);
				}

				rowIndex++;
			}
		} else {
			List<Method> methodList = getMethodList(modelClass, excelData.getFieldNames());
			Label label;
			for (int j = 0; j < dataList.size(); j++) {
				Object data = dataList.get(j);
				label = new Label(0, rowIndex + currentRowIndex, (j + 1) + "", stringCellFormat);
				ws.addCell(label);
				for (int k = 0; k < methodList.size(); k++) {
					Object fieldValue = null;
					try {
						fieldValue = methodList.get(k).invoke(data);
					} catch (Exception e) {
						// nothing to do
					}
					String fieldValueStr = "";
					if (fieldValue != null) {
						fieldValueStr = fieldValue.toString();
					}
					label = new Label(k + 1, rowIndex + currentRowIndex, fieldValueStr, cellFormats[k]);
					ws.addCell(label);
				}

				rowIndex++;
			}
		}

		return rowIndex;
	}

	private int setDataByFieldIndex(ExcelData excelData, List<?> dataList, CellFormat stringCellFormat,
									CellFormat[] cellFormats, WritableSheet ws, int currentRowIndex) throws WriteException {
		int rowIndex = 0;
		Label label;
		int[] fieldIndexs = excelData.getFieldIndexs();
		for (int j = 0; j < dataList.size(); j++) {
			Object[] data = (Object[]) dataList.get(j);
			label = new Label(0, rowIndex + currentRowIndex, (j + 1) + "", stringCellFormat);
			ws.addCell(label);
			for (int k = 0; k < fieldIndexs.length; k++) {
				String fieldValueStr = "";
				if (data[k] != null) {
					fieldValueStr = data[k].toString();
				}
				label = new Label(k + 1, rowIndex + currentRowIndex, fieldValueStr, cellFormats[k]);
				ws.addCell(label);
			}

			rowIndex++;
		}
		return rowIndex;
	}

	/**
	 * 按字段名称获得对象的get方法
	 *
	 * @param data
	 * @param fieldNames
	 * @return
	 * @throws Exception
	 */
	private List<Method> getMethodList(Class<?> modelClass, String[] fieldNames) {
		List<Method> methodList = new ArrayList<Method>();
		for (int i = 0; i < fieldNames.length; i++) {
			String fieldName = fieldNames[i];
			Method method = null;
			try {
				method = modelClass.getMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
			} catch (SecurityException e) {
				break;
			} catch (NoSuchMethodException e) {
				break;
			}

			methodList.add(method);
		}

		return methodList;
	}

}
